load libraries

load the FW functions


### CODE DIRECTLY FROM: https://burtmonroe.github.io/TextAsDataCourse/Tutorials/TADA-FightinWords.nb.html#

fwgroups <- function(dtm, groups, pair = NULL, weights = rep(1,nrow(dtm)), k.prior = .1) {
  
  weights[is.na(weights)] <- 0
  
  weights <- weights/mean(weights)
  
  zero.doc <- rowSums(dtm)==0 | weights==0
  zero.term <- colSums(dtm[!zero.doc,])==0
  
  dtm.nz <- apply(dtm[!zero.doc,!zero.term],2,"*", weights[!zero.doc])
  
  g.prior <- tcrossprod(rowSums(dtm.nz),colSums(dtm.nz))/sum(dtm.nz)
  
  # 
  
  g.posterior <- as.matrix(dtm.nz + k.prior*g.prior)
  
  groups <- groups[!zero.doc]
  groups <- droplevels(groups)
  
  g.adtm <- as.matrix(aggregate(x=g.posterior,by=list(groups=groups),FUN=sum)[,-1])
  rownames(g.adtm) <- levels(groups)
  
  g.ladtm <- log(g.adtm)
  
  g.delta <- t(scale( t(scale(g.ladtm, center=T, scale=F)), center=T, scale=F))
  
  g.adtm_w <- -sweep(g.adtm,1,rowSums(g.adtm)) # terms not w spoken by k
  g.adtm_k <- -sweep(g.adtm,2,colSums(g.adtm)) # w spoken by groups other than k
  g.adtm_kw <- sum(g.adtm) - g.adtm_w - g.adtm_k - g.adtm # total terms not w or k 
  
  g.se <- sqrt(1/g.adtm + 1/g.adtm_w + 1/g.adtm_k + 1/g.adtm_kw)
  
  g.zeta <- g.delta/g.se
  
  g.counts <- as.matrix(aggregate(x=dtm.nz, by = list(groups=groups), FUN=sum)[,-1])
  
  if (!is.null(pair)) {
    pr.delta <- t(scale( t(scale(g.ladtm[pair,], center = T, scale =F)), center=T, scale=F))
    pr.adtm_w <- -sweep(g.adtm[pair,],1,rowSums(g.adtm[pair,]))
    pr.adtm_k <- -sweep(g.adtm[pair,],2,colSums(g.adtm[pair,])) # w spoken by groups other than k
    pr.adtm_kw <- sum(g.adtm[pair,]) - pr.adtm_w - pr.adtm_k - g.adtm[pair,] # total terms not w or k
    pr.se <- sqrt(1/g.adtm[pair,] + 1/pr.adtm_w + 1/pr.adtm_k + 1/pr.adtm_kw)
    pr.zeta <- pr.delta/pr.se
    
    return(list(zeta=pr.zeta[1,], delta=pr.delta[1,],se=pr.se[1,], counts = colSums(dtm.nz), acounts = colSums(g.adtm)))
  } else {
    return(list(zeta=g.zeta,delta=g.delta,se=g.se,counts=g.counts,acounts=g.adtm))
  }
}

############## FIGHTIN' WORDS PLOTTING FUNCTION

# helper function
makeTransparent<-function(someColor, alpha=100)
{
  newColor<-col2rgb(someColor)
  apply(newColor, 2, function(curcoldata){rgb(red=curcoldata[1], green=curcoldata[2],
                                              blue=curcoldata[3],alpha=alpha, maxColorValue=255)})
}

fw.ggplot.groups <- function(fw.ch, groups.use = as.factor(rownames(fw.ch$zeta)), max.words = 50, max.countrank = 400, colorpalette=rep("black",length(groups.use)), sizescale=2, title="Comparison of Terms by Groups", subtitle = "", caption = "Group-specific terms are ordered by Fightin' Words statistic (Monroe, et al. 2008)") {
  if (is.null(dim(fw.ch$zeta))) {## two-group fw object consists of vectors, not matrices
    zetarankmat <- cbind(rank(-fw.ch$zeta),rank(fw.ch$zeta))
    colnames(zetarankmat) <- groups.use
    countrank <- rank(-(fw.ch$counts))
  } else {
    zetarankmat <- apply(-fw.ch$zeta[groups.use,],1,rank)
    countrank <- rank(-colSums(fw.ch$counts))
  }
  wideplotmat <- as_tibble(cbind(zetarankmat,countrank=countrank))
  wideplotmat$term=names(countrank)
  #rankplot <- gather(wideplotmat, party, zetarank, 1:ncol(zetarankmat))
  rankplot <- gather(wideplotmat, groups.use, zetarank, 1:ncol(zetarankmat))
  rankplot$plotsize <- sizescale*(50/(rankplot$zetarank))^(1/4)
  rankplot <- rankplot[rankplot$zetarank < max.words + 1 & rankplot$countrank<max.countrank+1,]
  rankplot$groups.use <- factor(rankplot$groups.use,levels=groups.use)
  
  p <- ggplot(rankplot, aes((nrow(rankplot)-countrank)^1, -(zetarank^1), colour=groups.use)) + 
    geom_point(show.legend=F,size=sizescale/2) + 
    theme_classic() +
    theme(axis.ticks=element_blank(), axis.text=element_blank() ) +
    ylim(-max.words,40) +
    facet_grid(groups.use ~ .) +
    geom_text_repel(aes(label = term), size = rankplot$plotsize, point.padding=.05,
                    box.padding = unit(0.20, "lines"), show.legend=F, max.overlaps = Inf) +
    scale_colour_manual(values = alpha(colorpalette, .7)) + 
#    labs(x="Terms used more frequently overall →", y="Terms used more frequently by group →",  title=title, subtitle=subtitle , caption = caption) 
    labs(x=paste("Terms used more frequently overall -->"), y=paste("Terms used more frequently by group -->"),  title=title, subtitle=subtitle , caption = caption) 
  
}

options(ggrepel.max.overlaps = Inf)

fw.keys <- function(fw.ch,n.keys=10) {
  n.groups <- nrow(fw.ch$zeta)
  keys <- matrix("",n.keys,n.groups)
  colnames(keys) <- rownames(fw.ch$zeta)
  
  for (g in 1:n.groups) {
    keys[,g] <- names(sort(fw.ch$zeta[g,],dec=T)[1:n.keys])
  }
  keys
}

Compare NYT Before and After “extremist” and other query terms

Load and clean the data

text_cleaner<-function(corpus){
  tempcorpus<-Corpus(VectorSource(corpus))
  tempcorpus<-tm_map(tempcorpus,
                    removePunctuation)
  tempcorpus<-tm_map(tempcorpus,
                    stripWhitespace)
  tempcorpus<-tm_map(tempcorpus,
                    removeNumbers)
  tempcorpus<-tm_map(tempcorpus,
                     removeWords, stopwords("english"))
  tempcorpus<-tm_map(tempcorpus, 
                    stemDocument)
  return(tempcorpus)
}
transformation drops documentstransformation drops documentstransformation drops documentstransformation drops documentstransformation drops documents

Calculate FW.

[1] 29844 14891
Document-feature matrix of: 6 documents, 14,891 features (100.0% sparse).
       features
docs    us struggl uptick american plot attack past month last week
  text1  1       1      1        1    1      0    0     0    0    0
  text2  0       0      0        0    0      1    1     1    1    1
  text3  0       0      0        0    0      0    0     0    0    0
  text4  0       0      0        0    0      0    0     0    0    0
  text5  0       0      0        0    0      0    0     0    0    0
  text6  0       0      0        0    0      0    0     0    0    0
[ reached max_nfeat ... 14,881 more features ]
Removing features occurring: 
  - fewer than 4 times: 10,569
  Total features removed: 10,569 (71.1%).
[1] 29844  4289

Get and show the top words per group by zeta.

library(knitr)
fwkeys.extrem <- fw.keys(fw.extrem, n.keys=20)
kable(fwkeys.extrem)
Context.after Context.before
group islam
hussein palestinian
attack war
parti iraqi
elect suspect
leader crack
jihad jemaah
milit crackdown
movement main
said alleg
guerrilla support
network evid
regim albanian
armi sept
politician prodemocraci
organ muslim
outsid megawati
univers battl
bomb prevent
rocket kosovo

Plot: Before in Blue, After in Red

p.fw.extrem <- fw.ggplot.groups(fw.extrem,sizescale=4,max.words=200,
                                max.countrank=400,colorpalette=c("red","blue"))
p.fw.extrem

Calculate by query item/search term


extrem_NYT.dfm.long$Query.item <- as.factor(extrem_NYT.dfm.long$Query.item)

top_n <-as.data.frame(sort(table(extrem_NYT.dfm.long$Query.item), decreasing = TRUE)[1:5]) 
dim(top_n)
[1] 5 2
colnames(top_n) <- c('term', 'Freq')
top_n

extrem_dtm_topn <- convert(e, to='data.frame')
extrem_dtm_topn$Number.of.hit <- extrem_NYT.dfm.long$Number.of.hit

topn_terms <- extrem_NYT.dfm.long %>%
      filter(Query.item %in% top_n$term)

extrem_dtm_topn_keep <- extrem_dtm_topn %>% 
    filter(Number.of.hit %in% topn_terms$Number.of.hit)

r <- sum(length(extrem_dtm_topn_keep))

extrem_dtm_topn_keep <- extrem_dtm_topn_keep[-c(1, r)]

fw.query_item <- fwgroups(extrem_dtm_topn_keep,groups = topn_terms$Query.item)
fwkeys.query_item <- fw.keys(fw.query_item, n.keys=15)
kable(fwkeys.query_item)
Islamic militants opposition Saddam terrorist
milit islam parti hussein attack
hama palestinian democrat iraqi organ
jihad kill leader iraq sept
group isra parliament presid unit
movement suspect elect regim state
extremist gaza vote saddam activ
front israel opposit baghdad alqaida
insurg attack lawmak un act
radic kashmir polit kuwait network
republ taliban main captur group
law arafat candid war laden
fundamentalist forc coalit oust bin
revolut pakistani conserv weapon suspect
somalia fire allianc bush connect
hardlin pakistan protest us financ

rm(r)
rm(extrem_dtm_topn)

p.fw.query_item <- fw.ggplot.groups(fw.query_item,sizescale=2,max.words=50,max.countrank=400)
p.fw.query_item


extrem_dtm_topn <- convert(e, to='data.frame')
extrem_dtm_topn$Number.of.hit <- extrem_NYT.dfm.long$Number.of.hit
extrem_dtm_topn$Context <- extrem_NYT.dfm.long$Context

topn_terms <- extrem_NYT.dfm.long %>%
      filter(Query.item %in% top_n$term)

extrem_dtm_topn_keep <- extrem_dtm_topn %>% 
    filter(Number.of.hit %in% topn_terms$Number.of.hit)

extrem_dtm_topn_keep_before <- extrem_dtm_topn_keep[grep("before",extrem_dtm_topn_keep$Context),]
extrem_dtm_topn_keep_after <- extrem_dtm_topn_keep[grep("after", extrem_dtm_topn_keep$Context),]

rr <-dim(topn_terms)[1]
r <- sum(length(extrem_dtm_topn_keep_before))
topn_terms_before <- topn_terms[seq(1,rr,2),]
topn_terms_after <- topn_terms[seq(2,rr,2),]

extrem_dtm_topn_keep_before <- extrem_dtm_topn_keep_before[-c(1, r-1, r)]
extrem_dtm_topn_keep_after <- extrem_dtm_topn_keep_after[-c(1, r-1, r)]

rm(rr)
rm(r)
rm(extrem_dtm_topn)

#############################################
fw.query_item_before <- fwgroups(extrem_dtm_topn_keep_before,groups = topn_terms_before$Query.item)
fwkeys.query_item_before <- fw.keys(fw.query_item_before, n.keys=15)
kable(fwkeys.query_item_before)
Islamic militants opposition Saddam terrorist
milit islam main iraqi sept
organ palestinian elect presid alqaida
strict kill polit iraq bin
radic suspect parliament war laden
somalia attack opposit saddam involv
hama taliban parti oust unit
secular isra minist baghdad suspect
hardlin muslim vote fall link
iran gaza democrat bush state
turkey armi prime hussein consid
extremist troop strong captur connect
malaysia israel percent regim appar
suprem pakistan despit usl osama
gaza forc poll former charg
fundamentalist arafat voic death list

p.fw.query_item_before <- fw.ggplot.groups(fw.query_item_before,sizescale=2,max.words=50,max.countrank=400,
                                           colorpalette = c('red', 'red','red','red','red'))
p.fw.query_item_before


#############################################
fw.query_item_after <- fwgroups(extrem_dtm_topn_keep_after,groups = topn_terms_after$Query.item)
fwkeys.query_item_after <- fw.keys(fw.query_item_after, n.keys=15)
kable(fwkeys.query_item_after)
Islamic militants opposition Saddam terrorist
milit isra parti hussein attack
group israel leader regim organ
jihad kashmir democrat iraq state
hama gaza vote iraqi unit
movement palestinian parliament un activ
law kill lawmak weapon group
insurg attack protest saddam act
extremist region elect son network
front southern candid captur bomb
revolut fire politician kuwait suspect
republ rocket coalit mass threat
court pakistani social baghdad financ
fundamentalist fight conserv us sept
news border win resolut link
agenc area opposit palac cell

p.fw.query_item_after <- fw.ggplot.groups(fw.query_item_after,sizescale=2,max.words=50,max.countrank=400,
                                           colorpalette = c('blue','blue','blue', 'blue','blue'))
p.fw.query_item_after

NA
NA

Calculate Parts of speech by before and after

Calculate FW and keys

### https://cran.r-project.org/web/packages/udpipe/vignettes/udpipe-usecase-postagging-lemmatisation.html

library(udpipe)

ud_model <- udpipe_download_model(language = "english")
ud_model <- udpipe_load_model(ud_model$file_model)

txt <-as.character(extrem_NYT.dfm.long$context.text)

x_udp <- udpipe_annotate(ud_model, x = txt, doc_id = seq_along(txt))
x <- as.data.frame(x_udp)

x$doc_id <-as.integer(x$doc_id)

x_odd.before <- x[x$doc_id %% 2 == 1,]
x_even.after <-x[x$doc_id %% 2 == 0, ]

A few barchart functions

Bar Charts from Functions Above

Cooccurences

Cooccurences (part 2)


Corrs <- function(df){
  df$id <- unique_identifier(df, fields = c("sentence_id", "doc_id"))
  dtm <- subset(df, upos %in% c("NOUN", "ADJ"))
  dtm <- document_term_frequencies(dtm, document = "id", term = "lemma")
  dtm <- document_term_matrix(dtm)
  dtm <- dtm_remove_lowfreq(dtm, minfreq = 5)
  termcorrelations <- dtm_cor(dtm)
  y <- as_cooccurrence(termcorrelations)
  y <- subset(y, term1 < term2 & abs(cooc) > 0.2)
  y <- y[order(abs(y$cooc), decreasing = TRUE), ]
  print(y[1:25,])
}


Corrs(x_odd.before)
Corrs(x_even.after)

home

LS0tCnRpdGxlOiAiRXh0cmVtKGlzdCArKSBGaWdodGluJyBXb3JkcyIKYXV0aG9yOiAiQnJlYW5uYSBFLiBHcmVlbiIKc3VidGl0bGU6IHByYWN0aWNpbmcKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICB0aGVtZTogdW5pdGVkCiAgICB0b2M6IHllcwotLS0KCiMjIGxvYWQgbGlicmFyaWVzCgpgYGB7ciwgZWNobz1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KHRtKQpgYGAKCiMjIGxvYWQgdGhlIEZXIGZ1bmN0aW9ucwoKYGBge3J9CgojIyMgQ09ERSBESVJFQ1RMWSBGUk9NOiBodHRwczovL2J1cnRtb25yb2UuZ2l0aHViLmlvL1RleHRBc0RhdGFDb3Vyc2UvVHV0b3JpYWxzL1RBREEtRmlnaHRpbldvcmRzLm5iLmh0bWwjCgpmd2dyb3VwcyA8LSBmdW5jdGlvbihkdG0sIGdyb3VwcywgcGFpciA9IE5VTEwsIHdlaWdodHMgPSByZXAoMSxucm93KGR0bSkpLCBrLnByaW9yID0gLjEpIHsKICAKICB3ZWlnaHRzW2lzLm5hKHdlaWdodHMpXSA8LSAwCiAgCiAgd2VpZ2h0cyA8LSB3ZWlnaHRzL21lYW4od2VpZ2h0cykKICAKICB6ZXJvLmRvYyA8LSByb3dTdW1zKGR0bSk9PTAgfCB3ZWlnaHRzPT0wCiAgemVyby50ZXJtIDwtIGNvbFN1bXMoZHRtWyF6ZXJvLmRvYyxdKT09MAogIAogIGR0bS5ueiA8LSBhcHBseShkdG1bIXplcm8uZG9jLCF6ZXJvLnRlcm1dLDIsIioiLCB3ZWlnaHRzWyF6ZXJvLmRvY10pCiAgCiAgZy5wcmlvciA8LSB0Y3Jvc3Nwcm9kKHJvd1N1bXMoZHRtLm56KSxjb2xTdW1zKGR0bS5ueikpL3N1bShkdG0ubnopCiAgCiAgIyAKICAKICBnLnBvc3RlcmlvciA8LSBhcy5tYXRyaXgoZHRtLm56ICsgay5wcmlvcipnLnByaW9yKQogIAogIGdyb3VwcyA8LSBncm91cHNbIXplcm8uZG9jXQogIGdyb3VwcyA8LSBkcm9wbGV2ZWxzKGdyb3VwcykKICAKICBnLmFkdG0gPC0gYXMubWF0cml4KGFnZ3JlZ2F0ZSh4PWcucG9zdGVyaW9yLGJ5PWxpc3QoZ3JvdXBzPWdyb3VwcyksRlVOPXN1bSlbLC0xXSkKICByb3duYW1lcyhnLmFkdG0pIDwtIGxldmVscyhncm91cHMpCiAgCiAgZy5sYWR0bSA8LSBsb2coZy5hZHRtKQogIAogIGcuZGVsdGEgPC0gdChzY2FsZSggdChzY2FsZShnLmxhZHRtLCBjZW50ZXI9VCwgc2NhbGU9RikpLCBjZW50ZXI9VCwgc2NhbGU9RikpCiAgCiAgZy5hZHRtX3cgPC0gLXN3ZWVwKGcuYWR0bSwxLHJvd1N1bXMoZy5hZHRtKSkgIyB0ZXJtcyBub3QgdyBzcG9rZW4gYnkgawogIGcuYWR0bV9rIDwtIC1zd2VlcChnLmFkdG0sMixjb2xTdW1zKGcuYWR0bSkpICMgdyBzcG9rZW4gYnkgZ3JvdXBzIG90aGVyIHRoYW4gawogIGcuYWR0bV9rdyA8LSBzdW0oZy5hZHRtKSAtIGcuYWR0bV93IC0gZy5hZHRtX2sgLSBnLmFkdG0gIyB0b3RhbCB0ZXJtcyBub3QgdyBvciBrIAogIAogIGcuc2UgPC0gc3FydCgxL2cuYWR0bSArIDEvZy5hZHRtX3cgKyAxL2cuYWR0bV9rICsgMS9nLmFkdG1fa3cpCiAgCiAgZy56ZXRhIDwtIGcuZGVsdGEvZy5zZQogIAogIGcuY291bnRzIDwtIGFzLm1hdHJpeChhZ2dyZWdhdGUoeD1kdG0ubnosIGJ5ID0gbGlzdChncm91cHM9Z3JvdXBzKSwgRlVOPXN1bSlbLC0xXSkKICAKICBpZiAoIWlzLm51bGwocGFpcikpIHsKICAgIHByLmRlbHRhIDwtIHQoc2NhbGUoIHQoc2NhbGUoZy5sYWR0bVtwYWlyLF0sIGNlbnRlciA9IFQsIHNjYWxlID1GKSksIGNlbnRlcj1ULCBzY2FsZT1GKSkKICAgIHByLmFkdG1fdyA8LSAtc3dlZXAoZy5hZHRtW3BhaXIsXSwxLHJvd1N1bXMoZy5hZHRtW3BhaXIsXSkpCiAgICBwci5hZHRtX2sgPC0gLXN3ZWVwKGcuYWR0bVtwYWlyLF0sMixjb2xTdW1zKGcuYWR0bVtwYWlyLF0pKSAjIHcgc3Bva2VuIGJ5IGdyb3VwcyBvdGhlciB0aGFuIGsKICAgIHByLmFkdG1fa3cgPC0gc3VtKGcuYWR0bVtwYWlyLF0pIC0gcHIuYWR0bV93IC0gcHIuYWR0bV9rIC0gZy5hZHRtW3BhaXIsXSAjIHRvdGFsIHRlcm1zIG5vdCB3IG9yIGsKICAgIHByLnNlIDwtIHNxcnQoMS9nLmFkdG1bcGFpcixdICsgMS9wci5hZHRtX3cgKyAxL3ByLmFkdG1fayArIDEvcHIuYWR0bV9rdykKICAgIHByLnpldGEgPC0gcHIuZGVsdGEvcHIuc2UKICAgIAogICAgcmV0dXJuKGxpc3QoemV0YT1wci56ZXRhWzEsXSwgZGVsdGE9cHIuZGVsdGFbMSxdLHNlPXByLnNlWzEsXSwgY291bnRzID0gY29sU3VtcyhkdG0ubnopLCBhY291bnRzID0gY29sU3VtcyhnLmFkdG0pKSkKICB9IGVsc2UgewogICAgcmV0dXJuKGxpc3QoemV0YT1nLnpldGEsZGVsdGE9Zy5kZWx0YSxzZT1nLnNlLGNvdW50cz1nLmNvdW50cyxhY291bnRzPWcuYWR0bSkpCiAgfQp9CgojIyMjIyMjIyMjIyMjIyBGSUdIVElOJyBXT1JEUyBQTE9UVElORyBGVU5DVElPTgoKIyBoZWxwZXIgZnVuY3Rpb24KbWFrZVRyYW5zcGFyZW50PC1mdW5jdGlvbihzb21lQ29sb3IsIGFscGhhPTEwMCkKewogIG5ld0NvbG9yPC1jb2wycmdiKHNvbWVDb2xvcikKICBhcHBseShuZXdDb2xvciwgMiwgZnVuY3Rpb24oY3VyY29sZGF0YSl7cmdiKHJlZD1jdXJjb2xkYXRhWzFdLCBncmVlbj1jdXJjb2xkYXRhWzJdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmx1ZT1jdXJjb2xkYXRhWzNdLGFscGhhPWFscGhhLCBtYXhDb2xvclZhbHVlPTI1NSl9KQp9Cgpmdy5nZ3Bsb3QuZ3JvdXBzIDwtIGZ1bmN0aW9uKGZ3LmNoLCBncm91cHMudXNlID0gYXMuZmFjdG9yKHJvd25hbWVzKGZ3LmNoJHpldGEpKSwgbWF4LndvcmRzID0gNTAsIG1heC5jb3VudHJhbmsgPSA0MDAsIGNvbG9ycGFsZXR0ZT1yZXAoImJsYWNrIixsZW5ndGgoZ3JvdXBzLnVzZSkpLCBzaXplc2NhbGU9MiwgdGl0bGU9IkNvbXBhcmlzb24gb2YgVGVybXMgYnkgR3JvdXBzIiwgc3VidGl0bGUgPSAiIiwgY2FwdGlvbiA9ICJHcm91cC1zcGVjaWZpYyB0ZXJtcyBhcmUgb3JkZXJlZCBieSBGaWdodGluJyBXb3JkcyBzdGF0aXN0aWMgKE1vbnJvZSwgZXQgYWwuIDIwMDgpIikgewogIGlmIChpcy5udWxsKGRpbShmdy5jaCR6ZXRhKSkpIHsjIyB0d28tZ3JvdXAgZncgb2JqZWN0IGNvbnNpc3RzIG9mIHZlY3RvcnMsIG5vdCBtYXRyaWNlcwogICAgemV0YXJhbmttYXQgPC0gY2JpbmQocmFuaygtZncuY2gkemV0YSkscmFuayhmdy5jaCR6ZXRhKSkKICAgIGNvbG5hbWVzKHpldGFyYW5rbWF0KSA8LSBncm91cHMudXNlCiAgICBjb3VudHJhbmsgPC0gcmFuaygtKGZ3LmNoJGNvdW50cykpCiAgfSBlbHNlIHsKICAgIHpldGFyYW5rbWF0IDwtIGFwcGx5KC1mdy5jaCR6ZXRhW2dyb3Vwcy51c2UsXSwxLHJhbmspCiAgICBjb3VudHJhbmsgPC0gcmFuaygtY29sU3Vtcyhmdy5jaCRjb3VudHMpKQogIH0KICB3aWRlcGxvdG1hdCA8LSBhc190aWJibGUoY2JpbmQoemV0YXJhbmttYXQsY291bnRyYW5rPWNvdW50cmFuaykpCiAgd2lkZXBsb3RtYXQkdGVybT1uYW1lcyhjb3VudHJhbmspCiAgI3JhbmtwbG90IDwtIGdhdGhlcih3aWRlcGxvdG1hdCwgcGFydHksIHpldGFyYW5rLCAxOm5jb2woemV0YXJhbmttYXQpKQogIHJhbmtwbG90IDwtIGdhdGhlcih3aWRlcGxvdG1hdCwgZ3JvdXBzLnVzZSwgemV0YXJhbmssIDE6bmNvbCh6ZXRhcmFua21hdCkpCiAgcmFua3Bsb3QkcGxvdHNpemUgPC0gc2l6ZXNjYWxlKig1MC8ocmFua3Bsb3QkemV0YXJhbmspKV4oMS80KQogIHJhbmtwbG90IDwtIHJhbmtwbG90W3JhbmtwbG90JHpldGFyYW5rIDwgbWF4LndvcmRzICsgMSAmIHJhbmtwbG90JGNvdW50cmFuazxtYXguY291bnRyYW5rKzEsXQogIHJhbmtwbG90JGdyb3Vwcy51c2UgPC0gZmFjdG9yKHJhbmtwbG90JGdyb3Vwcy51c2UsbGV2ZWxzPWdyb3Vwcy51c2UpCiAgCiAgcCA8LSBnZ3Bsb3QocmFua3Bsb3QsIGFlcygobnJvdyhyYW5rcGxvdCktY291bnRyYW5rKV4xLCAtKHpldGFyYW5rXjEpLCBjb2xvdXI9Z3JvdXBzLnVzZSkpICsgCiAgICBnZW9tX3BvaW50KHNob3cubGVnZW5kPUYsc2l6ZT1zaXplc2NhbGUvMikgKyAKICAgIHRoZW1lX2NsYXNzaWMoKSArCiAgICB0aGVtZShheGlzLnRpY2tzPWVsZW1lbnRfYmxhbmsoKSwgYXhpcy50ZXh0PWVsZW1lbnRfYmxhbmsoKSApICsKICAgIHlsaW0oLW1heC53b3Jkcyw0MCkgKwogICAgZmFjZXRfZ3JpZChncm91cHMudXNlIH4gLikgKwogICAgZ2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbCA9IHRlcm0pLCBzaXplID0gcmFua3Bsb3QkcGxvdHNpemUsIHBvaW50LnBhZGRpbmc9LjA1LAogICAgICAgICAgICAgICAgICAgIGJveC5wYWRkaW5nID0gdW5pdCgwLjIwLCAibGluZXMiKSwgc2hvdy5sZWdlbmQ9RiwgbWF4Lm92ZXJsYXBzID0gSW5mKSArCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGFscGhhKGNvbG9ycGFsZXR0ZSwgLjcpKSArIAojICAgIGxhYnMoeD0iVGVybXMgdXNlZCBtb3JlIGZyZXF1ZW50bHkgb3ZlcmFsbCDihpIiLCB5PSJUZXJtcyB1c2VkIG1vcmUgZnJlcXVlbnRseSBieSBncm91cCDihpIiLCAgdGl0bGU9dGl0bGUsIHN1YnRpdGxlPXN1YnRpdGxlICwgY2FwdGlvbiA9IGNhcHRpb24pIAogICAgbGFicyh4PXBhc3RlKCJUZXJtcyB1c2VkIG1vcmUgZnJlcXVlbnRseSBvdmVyYWxsIC0tPiIpLCB5PXBhc3RlKCJUZXJtcyB1c2VkIG1vcmUgZnJlcXVlbnRseSBieSBncm91cCAtLT4iKSwgIHRpdGxlPXRpdGxlLCBzdWJ0aXRsZT1zdWJ0aXRsZSAsIGNhcHRpb24gPSBjYXB0aW9uKSAKICAKfQoKb3B0aW9ucyhnZ3JlcGVsLm1heC5vdmVybGFwcyA9IEluZikKCmZ3LmtleXMgPC0gZnVuY3Rpb24oZncuY2gsbi5rZXlzPTEwKSB7CiAgbi5ncm91cHMgPC0gbnJvdyhmdy5jaCR6ZXRhKQogIGtleXMgPC0gbWF0cml4KCIiLG4ua2V5cyxuLmdyb3VwcykKICBjb2xuYW1lcyhrZXlzKSA8LSByb3duYW1lcyhmdy5jaCR6ZXRhKQogIAogIGZvciAoZyBpbiAxOm4uZ3JvdXBzKSB7CiAgICBrZXlzWyxnXSA8LSBuYW1lcyhzb3J0KGZ3LmNoJHpldGFbZyxdLGRlYz1UKVsxOm4ua2V5c10pCiAgfQogIGtleXMKfQpgYGAKCgojIyBDb21wYXJlIE5ZVCBCZWZvcmUgYW5kIEFmdGVyICJleHRyZW1pc3QiIGFuZCBvdGhlciBxdWVyeSB0ZXJtcwoKTG9hZCBhbmQgY2xlYW4gdGhlIGRhdGEKCiAgKiB0byBzdHJpbmcgJiBsb3dlciB0ZXh0CiAgKiBwaXZvdCB0byBsb25nIGZvcm1hdAogICogYXBwbHkgdGV4dF9jbGVhbmVyIHRvIG9uZSBjb2x1bW4gImNvbnRleHQudGV4dCIKCmBgYHtyLCAgcmVzdWx0cz0nYXNpcyd9CnRleHRfY2xlYW5lcjwtZnVuY3Rpb24oY29ycHVzKXsKICB0ZW1wY29ycHVzPC1Db3JwdXMoVmVjdG9yU291cmNlKGNvcnB1cykpCiAgdGVtcGNvcnB1czwtdG1fbWFwKHRlbXBjb3JwdXMsCiAgICAgICAgICAgICAgICAgICAgcmVtb3ZlUHVuY3R1YXRpb24pCiAgdGVtcGNvcnB1czwtdG1fbWFwKHRlbXBjb3JwdXMsCiAgICAgICAgICAgICAgICAgICAgc3RyaXBXaGl0ZXNwYWNlKQogIHRlbXBjb3JwdXM8LXRtX21hcCh0ZW1wY29ycHVzLAogICAgICAgICAgICAgICAgICAgIHJlbW92ZU51bWJlcnMpCiAgdGVtcGNvcnB1czwtdG1fbWFwKHRlbXBjb3JwdXMsCiAgICAgICAgICAgICAgICAgICAgIHJlbW92ZVdvcmRzLCBzdG9wd29yZHMoImVuZ2xpc2giKSkKICB0ZW1wY29ycHVzPC10bV9tYXAodGVtcGNvcnB1cywgCiAgICAgICAgICAgICAgICAgICAgc3RlbURvY3VtZW50KQogIHJldHVybih0ZW1wY29ycHVzKQp9CgpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFfQpleHRyZW1fTllULmRmbV9hbGwgPC1yZWFkLmRlbGltKCJ+L0RvY3VtZW50cy9HaXRIdWIvNzBjb3JyX2V4dHJlbWlzdF85NDEwX05ZVC50eHQiLCBoZWFkZXI9VFJVRSwgc2VwPSJcdCIpCnNldC5zZWVkKDIyMTcpCmV4dHJlbV9OWVQuZGZtIDwtIGFzLmRhdGEuZnJhbWUoc2FtcGxlX24oZXh0cmVtX05ZVC5kZm1fYWxsLCAxNTAwMCkpCnJtKGV4dHJlbV9OWVQuZGZtX2FsbCkKCmV4dHJlbV9OWVQuZGZtJENvbnRleHQuYmVmb3JlID0gbGFwcGx5KGV4dHJlbV9OWVQuZGZtJENvbnRleHQuYmVmb3JlLCB0b1N0cmluZykKZXh0cmVtX05ZVC5kZm0kQ29udGV4dC5iZWZvcmUgPSBsYXBwbHkoZXh0cmVtX05ZVC5kZm0kQ29udGV4dC5iZWZvcmUsIHRvbG93ZXIpCgpleHRyZW1fTllULmRmbSRDb250ZXh0LmFmdGVyID0gbGFwcGx5KGV4dHJlbV9OWVQuZGZtJENvbnRleHQuYWZ0ZXIsIHRvU3RyaW5nKQpleHRyZW1fTllULmRmbSRDb250ZXh0LmFmdGVyID0gbGFwcGx5KGV4dHJlbV9OWVQuZGZtJENvbnRleHQuYWZ0ZXIsIHRvbG93ZXIpCgpleHRyZW1fTllULmRmbSA8LSBleHRyZW1fTllULmRmbSAlPiUgZGlzdGluY3QoQ29udGV4dC5iZWZvcmUsIC5rZWVwX2FsbCA9IFRSVUUpCgpleHRyZW1fTllULmRmbS5sb25nIDwtIHBpdm90X2xvbmdlcihleHRyZW1fTllULmRmbSwgY29scz1jKENvbnRleHQuYmVmb3JlLCBDb250ZXh0LmFmdGVyKSwgbmFtZXNfdG8gPSAiQ29udGV4dCIsIHZhbHVlc190byA9ICJjb250ZXh0LnRleHQiKQoKZXh0cmVtX05ZVC5kZm0ubG9uZyRDb250ZXh0IDwtIGFzLmZhY3RvcihleHRyZW1fTllULmRmbS5sb25nJENvbnRleHQpCgpleHRyZW1lY29ycHVzIDwtdGV4dF9jbGVhbmVyKGV4dHJlbV9OWVQuZGZtLmxvbmckY29udGV4dC50ZXh0KQoKYGBgCgoKQ2FsY3VsYXRlIEZXLgoKYGBge3IsIGVjaG89RkFMU0V9CmxpYnJhcnkocXVhbnRlZGEpCgplIDwtIGRmbShleHRyZW1lY29ycHVzJGNvbnRlbnQpCmRpbShlKQpoZWFkKGUpCgplIDwtIGRmbV9zZWxlY3QoZSwgcGF0dGVybiA9IHN0b3B3b3JkcygiZW5nbGlzaCIpLCBzZWxlY3Rpb24gPSAicmVtb3ZlIikKZSA8LSBkZm1fc2VsZWN0KGUsIG1pbl9uY2hhciA9IDIpCmUgPC0gZGZtX3RyaW0oZSwgbWluX3Rlcm1mcmVxID0gNCwgbWluX2RvY2ZyZXEgPSAuMDUsIHZlcmJvc2U9VFJVRSkKCmRpbShlKQojIHNwYXJzaXR5KGUpCgpleHRyZW1fZHRtIDwtIGNvbnZlcnQoZSwgdG89J2RhdGEuZnJhbWUnKQoKZXh0cmVtX2R0bSA8LSBleHRyZW1fZHRtWy1jKDEpXQoKdyA8LSB3aGljaCggc2FwcGx5KGV4dHJlbV9kdG0sIGNsYXNzICkgPT0gJ2NoYXJhY3RlcicgKQoKZncuZXh0cmVtIDwtIGZ3Z3JvdXBzKGV4dHJlbV9kdG0sIGdyb3Vwcz1leHRyZW1fTllULmRmbS5sb25nJENvbnRleHQpCgpgYGAKCgpHZXQgYW5kIHNob3cgdGhlIHRvcCB3b3JkcyBwZXIgZ3JvdXAgYnkgemV0YS4KCmBgYHtyIGVjaG89VFJVRSwgcmVzdWx0cz0iYXNpcyJ9CmxpYnJhcnkoa25pdHIpCmZ3a2V5cy5leHRyZW0gPC0gZncua2V5cyhmdy5leHRyZW0sIG4ua2V5cz0yMCkKa2FibGUoZndrZXlzLmV4dHJlbSkKYGBgCgpQbG90OiBCZWZvcmUgaW4gQmx1ZSwgQWZ0ZXIgaW4gUmVkCgpgYGB7ciwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9NH0KcC5mdy5leHRyZW0gPC0gZncuZ2dwbG90Lmdyb3Vwcyhmdy5leHRyZW0sc2l6ZXNjYWxlPTQsbWF4LndvcmRzPTIwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXguY291bnRyYW5rPTQwMCxjb2xvcnBhbGV0dGU9YygicmVkIiwiYmx1ZSIpKQpwLmZ3LmV4dHJlbQpgYGAKCiMjIENhbGN1bGF0ZSBieSBxdWVyeSBpdGVtL3NlYXJjaCB0ZXJtCgpgYGB7cn0KCmV4dHJlbV9OWVQuZGZtLmxvbmckUXVlcnkuaXRlbSA8LSBhcy5mYWN0b3IoZXh0cmVtX05ZVC5kZm0ubG9uZyRRdWVyeS5pdGVtKQoKdG9wX24gPC1hcy5kYXRhLmZyYW1lKHNvcnQodGFibGUoZXh0cmVtX05ZVC5kZm0ubG9uZyRRdWVyeS5pdGVtKSwgZGVjcmVhc2luZyA9IFRSVUUpWzE6NV0pIApkaW0odG9wX24pCmNvbG5hbWVzKHRvcF9uKSA8LSBjKCd0ZXJtJywgJ0ZyZXEnKQp0b3BfbgoKZXh0cmVtX2R0bV90b3BuIDwtIGNvbnZlcnQoZSwgdG89J2RhdGEuZnJhbWUnKQpleHRyZW1fZHRtX3RvcG4kTnVtYmVyLm9mLmhpdCA8LSBleHRyZW1fTllULmRmbS5sb25nJE51bWJlci5vZi5oaXQKCnRvcG5fdGVybXMgPC0gZXh0cmVtX05ZVC5kZm0ubG9uZyAlPiUKICAgICAgZmlsdGVyKFF1ZXJ5Lml0ZW0gJWluJSB0b3BfbiR0ZXJtKQoKZXh0cmVtX2R0bV90b3BuX2tlZXAgPC0gZXh0cmVtX2R0bV90b3BuICU+JSAKICAgIGZpbHRlcihOdW1iZXIub2YuaGl0ICVpbiUgdG9wbl90ZXJtcyROdW1iZXIub2YuaGl0KQoKciA8LSBzdW0obGVuZ3RoKGV4dHJlbV9kdG1fdG9wbl9rZWVwKSkKCmV4dHJlbV9kdG1fdG9wbl9rZWVwIDwtIGV4dHJlbV9kdG1fdG9wbl9rZWVwWy1jKDEsIHIpXQoKZncucXVlcnlfaXRlbSA8LSBmd2dyb3VwcyhleHRyZW1fZHRtX3RvcG5fa2VlcCxncm91cHMgPSB0b3BuX3Rlcm1zJFF1ZXJ5Lml0ZW0pCmZ3a2V5cy5xdWVyeV9pdGVtIDwtIGZ3LmtleXMoZncucXVlcnlfaXRlbSwgbi5rZXlzPTE1KQprYWJsZShmd2tleXMucXVlcnlfaXRlbSkKCnJtKHIpCnJtKGV4dHJlbV9kdG1fdG9wbikKCnAuZncucXVlcnlfaXRlbSA8LSBmdy5nZ3Bsb3QuZ3JvdXBzKGZ3LnF1ZXJ5X2l0ZW0sc2l6ZXNjYWxlPTIsbWF4LndvcmRzPTUwLG1heC5jb3VudHJhbms9NDAwKQpwLmZ3LnF1ZXJ5X2l0ZW0KCmBgYAoKCgpgYGB7ciwgZWNobz1UUlVFLCByZXN1bHRzPSdhc2lzJ30KCmV4dHJlbV9kdG1fdG9wbiA8LSBjb252ZXJ0KGUsIHRvPSdkYXRhLmZyYW1lJykKZXh0cmVtX2R0bV90b3BuJE51bWJlci5vZi5oaXQgPC0gZXh0cmVtX05ZVC5kZm0ubG9uZyROdW1iZXIub2YuaGl0CmV4dHJlbV9kdG1fdG9wbiRDb250ZXh0IDwtIGV4dHJlbV9OWVQuZGZtLmxvbmckQ29udGV4dAoKdG9wbl90ZXJtcyA8LSBleHRyZW1fTllULmRmbS5sb25nICU+JQogICAgICBmaWx0ZXIoUXVlcnkuaXRlbSAlaW4lIHRvcF9uJHRlcm0pCgpleHRyZW1fZHRtX3RvcG5fa2VlcCA8LSBleHRyZW1fZHRtX3RvcG4gJT4lIAogICAgZmlsdGVyKE51bWJlci5vZi5oaXQgJWluJSB0b3BuX3Rlcm1zJE51bWJlci5vZi5oaXQpCgpleHRyZW1fZHRtX3RvcG5fa2VlcF9iZWZvcmUgPC0gZXh0cmVtX2R0bV90b3BuX2tlZXBbZ3JlcCgiYmVmb3JlIixleHRyZW1fZHRtX3RvcG5fa2VlcCRDb250ZXh0KSxdCmV4dHJlbV9kdG1fdG9wbl9rZWVwX2FmdGVyIDwtIGV4dHJlbV9kdG1fdG9wbl9rZWVwW2dyZXAoImFmdGVyIiwgZXh0cmVtX2R0bV90b3BuX2tlZXAkQ29udGV4dCksXQoKcnIgPC1kaW0odG9wbl90ZXJtcylbMV0KciA8LSBzdW0obGVuZ3RoKGV4dHJlbV9kdG1fdG9wbl9rZWVwX2JlZm9yZSkpCnRvcG5fdGVybXNfYmVmb3JlIDwtIHRvcG5fdGVybXNbc2VxKDEscnIsMiksXQp0b3BuX3Rlcm1zX2FmdGVyIDwtIHRvcG5fdGVybXNbc2VxKDIscnIsMiksXQoKZXh0cmVtX2R0bV90b3BuX2tlZXBfYmVmb3JlIDwtIGV4dHJlbV9kdG1fdG9wbl9rZWVwX2JlZm9yZVstYygxLCByLTEsIHIpXQpleHRyZW1fZHRtX3RvcG5fa2VlcF9hZnRlciA8LSBleHRyZW1fZHRtX3RvcG5fa2VlcF9hZnRlclstYygxLCByLTEsIHIpXQoKcm0ocnIpCnJtKHIpCnJtKGV4dHJlbV9kdG1fdG9wbikKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpmdy5xdWVyeV9pdGVtX2JlZm9yZSA8LSBmd2dyb3VwcyhleHRyZW1fZHRtX3RvcG5fa2VlcF9iZWZvcmUsZ3JvdXBzID0gdG9wbl90ZXJtc19iZWZvcmUkUXVlcnkuaXRlbSkKZndrZXlzLnF1ZXJ5X2l0ZW1fYmVmb3JlIDwtIGZ3LmtleXMoZncucXVlcnlfaXRlbV9iZWZvcmUsIG4ua2V5cz0xNSkKa2FibGUoZndrZXlzLnF1ZXJ5X2l0ZW1fYmVmb3JlKQoKcC5mdy5xdWVyeV9pdGVtX2JlZm9yZSA8LSBmdy5nZ3Bsb3QuZ3JvdXBzKGZ3LnF1ZXJ5X2l0ZW1fYmVmb3JlLHNpemVzY2FsZT0yLG1heC53b3Jkcz01MCxtYXguY291bnRyYW5rPTQwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9ycGFsZXR0ZSA9IGMoJ3JlZCcsICdyZWQnLCdyZWQnLCdyZWQnLCdyZWQnKSkKcC5mdy5xdWVyeV9pdGVtX2JlZm9yZQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCmZ3LnF1ZXJ5X2l0ZW1fYWZ0ZXIgPC0gZndncm91cHMoZXh0cmVtX2R0bV90b3BuX2tlZXBfYWZ0ZXIsZ3JvdXBzID0gdG9wbl90ZXJtc19hZnRlciRRdWVyeS5pdGVtKQpmd2tleXMucXVlcnlfaXRlbV9hZnRlciA8LSBmdy5rZXlzKGZ3LnF1ZXJ5X2l0ZW1fYWZ0ZXIsIG4ua2V5cz0xNSkKa2FibGUoZndrZXlzLnF1ZXJ5X2l0ZW1fYWZ0ZXIpCgpwLmZ3LnF1ZXJ5X2l0ZW1fYWZ0ZXIgPC0gZncuZ2dwbG90Lmdyb3Vwcyhmdy5xdWVyeV9pdGVtX2FmdGVyLHNpemVzY2FsZT0yLG1heC53b3Jkcz01MCxtYXguY291bnRyYW5rPTQwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9ycGFsZXR0ZSA9IGMoJ2JsdWUnLCdibHVlJywnYmx1ZScsICdibHVlJywnYmx1ZScpKQpwLmZ3LnF1ZXJ5X2l0ZW1fYWZ0ZXIKCgpgYGAKCgoKIyMgQ2FsY3VsYXRlIFBhcnRzIG9mIHNwZWVjaCBieSBiZWZvcmUgYW5kIGFmdGVyCgpDYWxjdWxhdGUgRlcgYW5kIGtleXMKYGBge3IsIHJlc3VsdHM9J2hpZGUnfQojIyMgaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3VkcGlwZS92aWduZXR0ZXMvdWRwaXBlLXVzZWNhc2UtcG9zdGFnZ2luZy1sZW1tYXRpc2F0aW9uLmh0bWwKCmxpYnJhcnkodWRwaXBlKQoKdWRfbW9kZWwgPC0gdWRwaXBlX2Rvd25sb2FkX21vZGVsKGxhbmd1YWdlID0gImVuZ2xpc2giKQoKdWRfbW9kZWwgPC0gdWRwaXBlX2xvYWRfbW9kZWwodWRfbW9kZWwkZmlsZV9tb2RlbCkKCnR4dCA8LWFzLmNoYXJhY3RlcihleHRyZW1fTllULmRmbS5sb25nJGNvbnRleHQudGV4dCkKCnhfdWRwIDwtIHVkcGlwZV9hbm5vdGF0ZSh1ZF9tb2RlbCwgeCA9IHR4dCwgZG9jX2lkID0gc2VxX2Fsb25nKHR4dCkpCnggPC0gYXMuZGF0YS5mcmFtZSh4X3VkcCkKCngkZG9jX2lkIDwtYXMuaW50ZWdlcih4JGRvY19pZCkKCnhfb2RkLmJlZm9yZSA8LSB4W3gkZG9jX2lkICUlIDIgPT0gMSxdCnhfZXZlbi5hZnRlciA8LXhbeCRkb2NfaWQgJSUgMiA9PSAwLCBdCgoKYGBgCgoKQSBmZXcgYmFyY2hhcnQgZnVuY3Rpb25zCgpgYGB7ciwgZWNobz1GQUxTRSwgcmVzdWx0cz0naGlkZSd9CmxpYnJhcnkobGF0dGljZSkKbGlicmFyeShsYXR0aWNlRXh0cmEpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShwbG90bHkpCmxpYnJhcnkocGRwKQpsaWJyYXJ5KHBhdGNod29yaykKCgojIyBVTklWRVJTQUwgUG9TClVQT1NfYmFyY2hhcnQgPC0gZnVuY3Rpb24oZGYxLCBkZjIpewogIHN0YXRzMSA8LSB0eHRfZnJlcShkZjEkdXBvcykKICBzdGF0czEka2V5IDwtIGZhY3RvcihzdGF0czEka2V5LCBsZXZlbHMgPSByZXYoc3RhdHMxJGtleSkpCiAgCiAgc3RhdHMyIDwtIHR4dF9mcmVxKGRmMiR1cG9zKQogIHN0YXRzMiRrZXkgPC0gZmFjdG9yKHN0YXRzMiRrZXksIGxldmVscyA9IHJldihzdGF0czIka2V5KSkKICAKICBjKGJhcmNoYXJ0KGtleSB+IGZyZXEsIGRhdGEgPSBzdGF0czEsIGNvbCA9ICJjYWRldGJsdWUiLCAKICAgICAgICBtYWluID0gIlVQT1MgKFVuaXZlcnNhbCBQYXJ0cyBvZiBTcGVlY2gpXG4gZnJlcXVlbmN5IG9mIG9jY3VycmVuY2U6IEJFRk9SRSB2cyBBRlRFUiIsIAogICAgICAgICB4bGFiID0gIkZyZXEiKSwgCiAgICBiYXJjaGFydChrZXkgfiBmcmVxLCBkYXRhID0gc3RhdHMyLCBjb2wgPSAgJ3NreWJsdWUnLAogICAgICAgICB4bGFiID0gIkZyZXEiKSkKfQoKCgojIyBOT1VOUwpOT1VOU19iYXJjaGFydCA8LSBmdW5jdGlvbihkZjEsIGRmMil7CiAgCiAgc3RhdHMxIDwtIHN1YnNldChkZjEsIHVwb3MgJWluJSBjKCJOT1VOIikpIAogIHN0YXRzMSA8LSB0eHRfZnJlcShzdGF0czEkdG9rZW4pCiAgc3RhdHMxJGtleSA8LSBmYWN0b3Ioc3RhdHMxJGtleSwgbGV2ZWxzID0gcmV2KHN0YXRzMSRrZXkpKQogIAogIHN0YXRzMiA8LSBzdWJzZXQoZGYyLCB1cG9zICVpbiUgYygiTk9VTiIpKSAKICBzdGF0czIgPC0gdHh0X2ZyZXEoc3RhdHMyJHRva2VuKQogIHN0YXRzMiRrZXkgPC0gZmFjdG9yKHN0YXRzMiRrZXksIGxldmVscyA9IHJldihzdGF0czIka2V5KSkKICAKICBjKGJhcmNoYXJ0KGtleSB+IGZyZXEsIGRhdGEgPSBoZWFkKHN0YXRzMSwgMjApLCBjb2wgPSAiY2FkZXRibHVlIiwgCiAgICAgICAgICAgbWFpbiA9ICJNb3N0IG9jY3VycmluZyBub3VuczogQkVGT1JFIHZzIEFGVEVSIiwgeGxhYiA9ICJGcmVxIiksCiAgICAgIGJhcmNoYXJ0KGtleSB+IGZyZXEsIGRhdGEgPSBoZWFkKHN0YXRzMiwgMjApLCBjb2wgPSAic2t5Ymx1ZSIsIAogICAgICAgICAgICB4bGFiID0gIkZyZXEiKSkKfQoKIyMgQURKRUNUSVZFUwpBREpfYmFyY2hhcnQgPC0gZnVuY3Rpb24oZGYxLCBkZjIpewogIAogIHN0YXRzMSA8LSBzdWJzZXQoZGYxLCB1cG9zICVpbiUgYygiQURKIikpIAogIHN0YXRzMSA8LSB0eHRfZnJlcShzdGF0czEkdG9rZW4pCiAgc3RhdHMxJGtleSA8LSBmYWN0b3Ioc3RhdHMxJGtleSwgbGV2ZWxzID0gcmV2KHN0YXRzMSRrZXkpKQogIAogIHN0YXRzMiA8LSBzdWJzZXQoZGYyLCB1cG9zICVpbiUgYygiQURKIikpIAogIHN0YXRzMiA8LSB0eHRfZnJlcShzdGF0czIkdG9rZW4pCiAgc3RhdHMyJGtleSA8LSBmYWN0b3Ioc3RhdHMyJGtleSwgbGV2ZWxzID0gcmV2KHN0YXRzMiRrZXkpKQogIAogIGMoYmFyY2hhcnQoa2V5IH4gZnJlcSwgZGF0YSA9IGhlYWQoc3RhdHMxLCAyMCksIGNvbCA9ICJjYWRldGJsdWUiLCAKICAgICAgICAgICBtYWluID0gIk1vc3Qgb2NjdXJyaW5nIGFkamVjdGl2ZXM6IEJFRk9SRSB2cyBBRlRFUiIsIHhsYWIgPSAiRnJlcSIpLAogICAgICBiYXJjaGFydChrZXkgfiBmcmVxLCBkYXRhID0gaGVhZChzdGF0czIsIDIwKSwgY29sID0gInNreWJsdWUiLCAKICAgICAgICAgeGxhYiA9ICJGcmVxIikpCn0KCiMjIFVzaW5nIFJBS0UgdG8gZmluZCBrZXl3b3JkcwpSQUtFX0tXX2JhcmNoYXJ0IDwtIGZ1bmN0aW9uKGRmMSxkZjIpewogIAogIHN0YXRzMSA8LSBrZXl3b3Jkc19yYWtlKHggPSBkZjEsIHRlcm0gPSAibGVtbWEiLCBncm91cCA9ICJkb2NfaWQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgIHJlbGV2YW50ID0gZGYxJHVwb3MgJWluJSBjKCJOT1VOIiwgIkFESiIpKQogIHN0YXRzMSRrZXkgPC0gZmFjdG9yKHN0YXRzMSRrZXl3b3JkLCBsZXZlbHMgPSByZXYoc3RhdHMxJGtleXdvcmQpKQogIAogIHN0YXRzMiA8LSBrZXl3b3Jkc19yYWtlKHggPSBkZjIsIHRlcm0gPSAibGVtbWEiLCBncm91cCA9ICJkb2NfaWQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgIHJlbGV2YW50ID0gZGYyJHVwb3MgJWluJSBjKCJOT1VOIiwgIkFESiIpKQogIHN0YXRzMiRrZXkgPC0gZmFjdG9yKHN0YXRzMiRrZXl3b3JkLCBsZXZlbHMgPSByZXYoc3RhdHMyJGtleXdvcmQpKQogIAogIAogIGMoYmFyY2hhcnQoa2V5IH4gcmFrZSwgZGF0YSA9IGhlYWQoc3Vic2V0KHN0YXRzMSwgZnJlcSA+IDMpLCAyMCksIGNvbCA9ICJjYWRldGJsdWUiLCAKICAgICAgICAgICBtYWluID0gIktleXdvcmRzIGlkZW50aWZpZWQgYnkgUkFLRTogQkVGT1JFIHZzIEFGVEVSIiwgCiAgICAgICAgICAgeGxhYiA9ICJSYWtlIiksCiAgICBiYXJjaGFydChrZXkgfiByYWtlLCBkYXRhID0gaGVhZChzdWJzZXQoc3RhdHMyLCBmcmVxID4gMyksIDIwKSwgY29sID0gInNreWJsdWUiLCAKICAgICAgICAgICB4bGFiID0gIlJha2UiKSkKfQoKIyMgVXNpbmcgUG9pbnR3aXNlIE11dHVhbCBJbmZvcm1hdGlvbiBDb2xsb2NhdGlvbnMKUFdJX2JhcmNoYXJ0IDwtIGZ1bmN0aW9uKGRmMSwgZGYyKXsKICAKICBkZjEkd29yZCA8LSB0b2xvd2VyKGRmMSR0b2tlbikKICBzdGF0czEgPC0ga2V5d29yZHNfY29sbG9jYXRpb24oeCA9IGRmMSwgdGVybSA9ICJ3b3JkIiwgZ3JvdXAgPSAiZG9jX2lkIikKICBzdGF0czEka2V5IDwtIGZhY3RvcihzdGF0czEka2V5d29yZCwgbGV2ZWxzID0gcmV2KHN0YXRzMSRrZXl3b3JkKSkKICAKICBkZjIkd29yZCA8LSB0b2xvd2VyKGRmMiR0b2tlbikKICBzdGF0czIgPC0ga2V5d29yZHNfY29sbG9jYXRpb24oeCA9IGRmMiwgdGVybSA9ICJ3b3JkIiwgZ3JvdXAgPSAiZG9jX2lkIikKICBzdGF0czIka2V5IDwtIGZhY3RvcihzdGF0czIka2V5d29yZCwgbGV2ZWxzID0gcmV2KHN0YXRzMiRrZXl3b3JkKSkKICAKICBjKGJhcmNoYXJ0KGtleSB+IHBtaSwgZGF0YSA9IGhlYWQoc3Vic2V0KHN0YXRzMSwgZnJlcSA+IDMpLCAyMCksIGNvbCA9ICJjYWRldGJsdWUiLCAKICAgICAgICAgICBtYWluID0gIktleXdvcmRzIGlkZW50aWZpZWQgYnkgUE1JIENvbGxvY2F0aW9uOiBCRUZPUkUgdnMgQUZURVIiLCAKICAgICAgICAgICB4bGFiID0gIlBNSSAoUG9pbnR3aXNlIE11dHVhbCBJbmZvcm1hdGlvbikiKSwKICAgICAgYmFyY2hhcnQoa2V5IH4gcG1pLCBkYXRhID0gaGVhZChzdWJzZXQoc3RhdHMyLCBmcmVxID4gMyksIDIwKSwgY29sID0gInNreWJsdWUiLCAKICAgICAgICAgICB4bGFiID0gIlBNSSAoUG9pbnR3aXNlIE11dHVhbCBJbmZvcm1hdGlvbikiKSkKfQoKIyMgVXNpbmcgYSBzZXF1ZW5jZSBvZiBQT1MgdGFncyAobm91biBwaHJhc2VzIC8gdmVyYiBwaHJhc2VzKQpQT1NfYmFyY2hhcnQgPC0gZnVuY3Rpb24oZGYxLCBkZjIpewogIAogIGRmMSRwaHJhc2VfdGFnIDwtIGFzX3BocmFzZW1hY2hpbmUoZGYxJHVwb3MsIHR5cGUgPSAidXBvcyIpCiAgc3RhdHMxIDwtIGtleXdvcmRzX3BocmFzZXMoeCA9IGRmMSRwaHJhc2VfdGFnLCB0ZXJtID0gdG9sb3dlcihkZjEkdG9rZW4pLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSAiKEF8TikqTihQK0QqKEF8TikqTikqIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpc19yZWdleCA9IFRSVUUsIGRldGFpbGVkID0gRkFMU0UpCiAgc3RhdHMxIDwtIHN1YnNldChzdGF0czEsIG5ncmFtID4gMSAmIGZyZXEgPiAzKQogIHN0YXRzMSRrZXkgPC0gZmFjdG9yKHN0YXRzMSRrZXl3b3JkLCBsZXZlbHMgPSByZXYoc3RhdHMxJGtleXdvcmQpKQogIAogIGRmMiRwaHJhc2VfdGFnIDwtIGFzX3BocmFzZW1hY2hpbmUoZGYyJHVwb3MsIHR5cGUgPSAidXBvcyIpCiAgc3RhdHMyIDwtIGtleXdvcmRzX3BocmFzZXMoeCA9IGRmMiRwaHJhc2VfdGFnLCB0ZXJtID0gdG9sb3dlcihkZjIkdG9rZW4pLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSAiKEF8TikqTihQK0QqKEF8TikqTikqIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpc19yZWdleCA9IFRSVUUsIGRldGFpbGVkID0gRkFMU0UpCiAgc3RhdHMyIDwtIHN1YnNldChzdGF0czIsIG5ncmFtID4gMSAmIGZyZXEgPiAzKQogIHN0YXRzMiRrZXkgPC0gZmFjdG9yKHN0YXRzMiRrZXl3b3JkLCBsZXZlbHMgPSByZXYoc3RhdHMyJGtleXdvcmQpKQogIAogIGMoYmFyY2hhcnQoa2V5IH4gZnJlcSwgZGF0YSA9IGhlYWQoc3RhdHMxLCAyMCksIGNvbCA9ICJjYWRldGJsdWUiLCAKICAgICAgICAgICBtYWluID0gIktleXdvcmRzIC0gc2ltcGxlIG5vdW4gcGhyYXNlczogQkVGT1JFIHZzIEFGVEVSIiwgeGxhYiA9ICJGcmVxdWVuY3kiKSwKICAgICAgYmFyY2hhcnQoa2V5IH4gZnJlcSwgZGF0YSA9IGhlYWQoc3RhdHMyLCAyMCksIGNvbCA9ICJza3libHVlIiwgCiAgICAgICAgICAgICAgIHhsYWIgPSAiRnJlcXVlbmN5IikpCn0KYGBgCgoKIyMgQmFyIENoYXJ0cyBmcm9tIEZ1bmN0aW9ucyBBYm92ZQoKYGBge3IsIGVjaG89RkFMU0UsIHJlc3VsdHM9J2FzaXMnfQoKVVBPU19iYXJjaGFydCh4X29kZC5iZWZvcmUsIHhfZXZlbi5hZnRlcikKTk9VTlNfYmFyY2hhcnQoeF9vZGQuYmVmb3JlLCB4X2V2ZW4uYWZ0ZXIpCkFESl9iYXJjaGFydCh4X29kZC5iZWZvcmUsIHhfZXZlbi5hZnRlcikKUkFLRV9LV19iYXJjaGFydCh4X29kZC5iZWZvcmUsIHhfZXZlbi5hZnRlcikKUFdJX2JhcmNoYXJ0KHhfb2RkLmJlZm9yZSwgeF9ldmVuLmFmdGVyKQpQT1NfYmFyY2hhcnQoeF9vZGQuYmVmb3JlLCB4X2V2ZW4uYWZ0ZXIpCgpgYGAKCgojIyBDb29jY3VyZW5jZXMKCmBgYHtyLCBlY2hvPUZBTFNFfQoKQ09fT0Nfbm91bl9hZGpfc2FtZV9zZW50LmJlZm9yZSA8LSBmdW5jdGlvbihkZjEpewogIAogIGxpYnJhcnkoaWdyYXBoKQogIGxpYnJhcnkoZ2dyYXBoKQogIGxpYnJhcnkoZ2dwbG90MikKICAKICBjb29jIDwtIGNvb2NjdXJyZW5jZSh4ID0gc3Vic2V0KGRmMSwgdXBvcyAlaW4lIGMoIk5PVU4iLCAiQURKIikpLCAKICAgICAgICAgICAgICAgICAgICAgICB0ZXJtID0gImxlbW1hIiwgCiAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBjKCJkb2NfaWQiLCAicGFyYWdyYXBoX2lkIiwgInNlbnRlbmNlX2lkIikpCgogIHdvcmRuZXR3b3JrIDwtIGhlYWQoY29vYywgNDApCiAgd29yZG5ldHdvcmsgPC0gZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKHdvcmRuZXR3b3JrKQogIAogIGdncmFwaCh3b3JkbmV0d29yaywgbGF5b3V0ID0gImZyIikgKwogICAgZ2VvbV9lZGdlX2xpbmsoYWVzKHdpZHRoID0gY29vYywgZWRnZV9hbHBoYSA9IGNvb2MpLCBlZGdlX2NvbG91ciA9ICJwaW5rIikgKwogICAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gbmFtZSksIGNvbCA9ICJkYXJrZ3JlZW4iLCBzaXplID0gNCkgKwogICAgdGhlbWVfZ3JhcGgoYmFzZV9mYW1pbHkgPSAiQXJpYWwgTmFycm93IikgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBsYWJzKHRpdGxlID0gIkNvb2NjdXJyZW5jZXMgd2l0aGluIHNlbnRlbmNlOiBCRUZPUkUiLCBzdWJ0aXRsZSA9ICJOb3VucyAmIEFkamVjdGl2ZSIpCiAgCn0KCkNPX09DX25vdW5fYWRqX3NhbWVfc2VudC5hZnRlciA8LSBmdW5jdGlvbihkZjIpewogIAogIGxpYnJhcnkoaWdyYXBoKQogIGxpYnJhcnkoZ2dyYXBoKQogIGxpYnJhcnkoZ2dwbG90MikKICAKICBjb29jIDwtIGNvb2NjdXJyZW5jZSh4ID0gc3Vic2V0KGRmMiwgdXBvcyAlaW4lIGMoIk5PVU4iLCAiQURKIikpLCAKICAgICAgICAgICAgICAgICAgICAgICB0ZXJtID0gImxlbW1hIiwgCiAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBjKCJkb2NfaWQiLCAicGFyYWdyYXBoX2lkIiwgInNlbnRlbmNlX2lkIikpCgogIHdvcmRuZXR3b3JrIDwtIGhlYWQoY29vYywgNDApCiAgd29yZG5ldHdvcmsgPC0gZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKHdvcmRuZXR3b3JrKQogIAogIGdncmFwaCh3b3JkbmV0d29yaywgbGF5b3V0ID0gImZyIikgKwogICAgZ2VvbV9lZGdlX2xpbmsoYWVzKHdpZHRoID0gY29vYywgZWRnZV9hbHBoYSA9IGNvb2MpLCBlZGdlX2NvbG91ciA9ICJsaWdodGdyZWVuIikgKwogICAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gbmFtZSksIGNvbCA9ICJkYXJrYmx1ZSIsIHNpemUgPSA0KSArCiAgICB0aGVtZV9ncmFwaChiYXNlX2ZhbWlseSA9ICJBcmlhbCBOYXJyb3ciKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIGxhYnModGl0bGUgPSAiQ29vY2N1cnJlbmNlcyB3aXRoaW4gc2VudGVuY2U6IEFGVEVSIiwgc3VidGl0bGUgPSAiTm91bnMgJiBBZGplY3RpdmUiKQogIAp9CgoKQ09fT0Nfbm91bl9hZGpfc2FtZV9zZW50LmJlZm9yZSh4X29kZC5iZWZvcmUpCkNPX09DX25vdW5fYWRqX3NhbWVfc2VudC5hZnRlcih4X2V2ZW4uYWZ0ZXIpCgoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpDT19PQ19ub3VuX2Fkal9mb2xsb3dpbmcuYmVmb3JlIDwtIGZ1bmN0aW9uKGRmKXsKICBjb29jIDwtIGNvb2NjdXJyZW5jZShkZiRsZW1tYSwgcmVsZXZhbnQgPSBkZiR1cG9zICVpbiUgYygiTk9VTiIsICJBREoiKSwgc2tpcGdyYW0gPSAxKQogIGhlYWQoY29vYykKICAKICB3b3JkbmV0d29yayA8LSBoZWFkKGNvb2MsIDE1KQogIHdvcmRuZXR3b3JrIDwtIGdyYXBoX2Zyb21fZGF0YV9mcmFtZSh3b3JkbmV0d29yaykKICBnZ3JhcGgod29yZG5ldHdvcmssIGxheW91dCA9ICJmciIpICsKICAgIGdlb21fZWRnZV9saW5rKGFlcyh3aWR0aCA9IGNvb2MsIGVkZ2VfYWxwaGEgPSBjb29jKSwgZWRnZV9jb2xvdXIgPSAibGlnaHRncmVlbiIpICsKICAgIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IG5hbWUpLCBjb2wgPSAiZGFya2dyZWVuIiwgc2l6ZSA9IDQpICsKICAgIHRoZW1lX2dyYXBoKGJhc2VfZmFtaWx5ID0gIkFyaWFsIE5hcnJvdyIpICsKICAgIGxhYnModGl0bGUgPSAiV29yZHMgZm9sbG93aW5nIG9uZSBhbm90aGVyOiBCRUZPUkUiLCBzdWJ0aXRsZSA9ICJOb3VucyAmIEFkamVjdGl2ZSIpCn0KCkNPX09DX25vdW5fYWRqX2ZvbGxvd2luZy5hZnRlciA8LSBmdW5jdGlvbihkZil7CiAgY29vYyA8LSBjb29jY3VycmVuY2UoZGYkbGVtbWEsIHJlbGV2YW50ID0gZGYkdXBvcyAlaW4lIGMoIk5PVU4iLCAiQURKIiksIHNraXBncmFtID0gMSkKICBoZWFkKGNvb2MpCiAgCiAgd29yZG5ldHdvcmsgPC0gaGVhZChjb29jLCAxNSkKICB3b3JkbmV0d29yayA8LSBncmFwaF9mcm9tX2RhdGFfZnJhbWUod29yZG5ldHdvcmspCiAgZ2dyYXBoKHdvcmRuZXR3b3JrLCBsYXlvdXQgPSAiZnIiKSArCiAgICBnZW9tX2VkZ2VfbGluayhhZXMod2lkdGggPSBjb29jLCBlZGdlX2FscGhhID0gY29vYyksIGVkZ2VfY29sb3VyID0gInNreWJsdWUiKSArCiAgICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgY29sID0gImRhcmtibHVlIiwgc2l6ZSA9IDQpICsKICAgIHRoZW1lX2dyYXBoKGJhc2VfZmFtaWx5ID0gIkFyaWFsIE5hcnJvdyIpICsKICAgIGxhYnModGl0bGUgPSAiV29yZHMgZm9sbG93aW5nIG9uZSBhbm90aGVyOiBBRlRFUiIsIHN1YnRpdGxlID0gIk5vdW5zICYgQWRqZWN0aXZlIikKfQoKCkNPX09DX25vdW5fYWRqX2ZvbGxvd2luZy5iZWZvcmUoeF9vZGQuYmVmb3JlKQpDT19PQ19ub3VuX2Fkal9mb2xsb3dpbmcuYWZ0ZXIoeF9ldmVuLmFmdGVyKQoKYGBgCgojIyBDb29jY3VyZW5jZXMgKHBhcnQgMikKYGBge3J9CgpDb3JycyA8LSBmdW5jdGlvbihkZil7CiAgZGYkaWQgPC0gdW5pcXVlX2lkZW50aWZpZXIoZGYsIGZpZWxkcyA9IGMoInNlbnRlbmNlX2lkIiwgImRvY19pZCIpKQogIGR0bSA8LSBzdWJzZXQoZGYsIHVwb3MgJWluJSBjKCJOT1VOIiwgIkFESiIpKQogIGR0bSA8LSBkb2N1bWVudF90ZXJtX2ZyZXF1ZW5jaWVzKGR0bSwgZG9jdW1lbnQgPSAiaWQiLCB0ZXJtID0gImxlbW1hIikKICBkdG0gPC0gZG9jdW1lbnRfdGVybV9tYXRyaXgoZHRtKQogIGR0bSA8LSBkdG1fcmVtb3ZlX2xvd2ZyZXEoZHRtLCBtaW5mcmVxID0gNSkKICB0ZXJtY29ycmVsYXRpb25zIDwtIGR0bV9jb3IoZHRtKQogIHkgPC0gYXNfY29vY2N1cnJlbmNlKHRlcm1jb3JyZWxhdGlvbnMpCiAgeSA8LSBzdWJzZXQoeSwgdGVybTEgPCB0ZXJtMiAmIGFicyhjb29jKSA+IDAuMikKICB5IDwtIHlbb3JkZXIoYWJzKHkkY29vYyksIGRlY3JlYXNpbmcgPSBUUlVFKSwgXQogIHByaW50KHlbMToyNSxdKQp9CgoKQ29ycnMoeF9vZGQuYmVmb3JlKQpDb3Jycyh4X2V2ZW4uYWZ0ZXIpCmBgYAoKCmBgYHtyIGZpbmFsfQpgYGAKCltob21lXSguLykK